using System;

namespace MLSharp.Cpn
{
	/// <summary>
	/// Options for creating counter-propagation networks.
	/// </summary>
	public class CpnNeuralNetOptions
	{
		#region Public Properties

		/// <summary>
		/// The starting error rate for the lower level of the network.
		/// </summary>
		/// <value>The default is 1.0.</value>
		public double LowerLevelStartingEta { get; set; }

		/// <summary>
		/// The ending error rate for the lower level of the network.
		/// </summary>
		/// <value>The default is 0.1.</value>
		public double LowerLevelEndingEta { get; set; }

		/// <summary>
		/// The step amount for each iteration of training for the lower level.
		/// </summary>
		/// <value>The default is -0.1.</value>
		public double LowerLevelStep { get; set; }

		/// <summary>
		/// The number of training iterations for each step of training for the lower level.
		/// </summary>
		/// <value>The default is 10.</value>
		public int LowerLevelIterationsPerEta { get; set; }

		/// <summary>
		/// A seed for CPN's random number generator.
		/// </summary>
		/// <value>The default is 0.</value>
		/// <remarks>
		/// Using a static seed means that you will get the same network 
		/// given a fixed set of inputs.  If this is not desireable, generate
		/// a random seed.
		/// </remarks>
		public int Seed { get; set; }

		/// <summary>
		/// The starting error rate for the upper level of the network.
		/// </summary>
		/// <value>The default is 1.0.</value>
		public double UpperLevelStartingEta { get; set; }

		/// <summary>
		/// The ending error rate for the upper level of the network.
		/// </summary>
		/// <value>The default is 0.1.</value>
		public double UpperLevelEndingEta { get; set; }

		/// <summary>
		/// The step amount for each iteration of training for the upper level.
		/// </summary>
		/// <value>The default is -0.1.</value>
		public double UpperLevelStep { get; set; }

		/// <summary>
		/// The number of training iterations for each step of training for the upper level.
		/// </summary>
		/// <value>The default is 10.</value>
		public int UpperLevelIterationsPerEta { get; set; }

		/// <summary>
		/// The number of neurons in the middle layer of the network.
		/// </summary>
		/// <remarks>
		/// Setting this to 0 will result in a network with twice as many nodes in the middle
		/// layer as there are input variables.
		/// </remarks>
		/// <value>The default is 0.</value>
		public int NumberOfNeurons { get; set; }

		#endregion

		#region Public Constructors

		/// <summary>
		/// Sets all properties to their defaults.
		/// </summary>
		public CpnNeuralNetOptions()
		{
			LowerLevelStartingEta = 1.0;
			LowerLevelEndingEta = 0.1;
			LowerLevelStep = -0.1;
			LowerLevelIterationsPerEta = 10;
			Seed = 0;
			UpperLevelStartingEta = 1.0;
			UpperLevelEndingEta = 0.1;
			UpperLevelStep = -0.1;
			UpperLevelIterationsPerEta = 10;
			NumberOfNeurons = 0;
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Verifies that all parameters are set to valid values.
		/// </summary>
		public void Validate()
		{
			//Verify that the step values are in the same direction as the difference between the starting
			//and ending ETA values
			if (LowerLevelStep * (LowerLevelStartingEta - LowerLevelEndingEta) > 0)
			{
				throw new InvalidOperationException(
					"LowerLevelStep is not in the direction implied by the starting and ending ETA values.");
			}

			if (UpperLevelStep * (UpperLevelStartingEta - UpperLevelEndingEta) > 0)
			{
				throw new InvalidOperationException(
					"UpperLevelStep is not in the direction implied by the starting and ending ETA values.");
			}

			if (NumberOfNeurons < 0)
			{
				throw new InvalidOperationException("NumberOfNeurons should be greater than or equal to zero.");
			}

			if (UpperLevelIterationsPerEta <= 0)
			{
				throw new InvalidOperationException("UpperLevelIterationsPerEta should be positive.");
			}

			if (LowerLevelIterationsPerEta <= 0)
			{
				throw new InvalidOperationException("LowerLevelIterationsPerEta should be positive.");
			}
		}

		#endregion
	}
}